#include "thirdparty/logging/logger_stream.h"
#include "thirdparty/dtoa-benchmark/src/floaxie/ftoa.h"
namespace logging {

const char digits[] = "9876543210123456789";
const char* zero = digits + 9;
static_assert(sizeof(digits) == 20, "wrong number of digits");

const char digitsHex[] = "0123456789ABCDEF";
static_assert(sizeof digitsHex == 17, "wrong number of digitsHex");

// Efficient Integer to String Conversions, by Matthew Wilson.
template<typename T>
size_t Convert(char buf[], T value)
{
  T i = value;
  char* p = buf;

  do
  {
    int lsd = static_cast<int>(i % 10);
    i /= 10;
    *p++ = zero[lsd];
  } while (i != 0);

  if (value < 0)
  {
    *p++ = '-';
  }
  *p = '\0';
  std::reverse(buf, p);

  return p - buf;
}

size_t ConvertHex(char buf[], uintptr_t value)
{
  uintptr_t i = value;
  char* p = buf;

  do
  {
    int lsd = static_cast<int>(i % 16);
    i /= 16;
    *p++ = digitsHex[lsd];
  } while (i != 0);

  *p = '\0';
  std::reverse(buf, p);

  return p - buf;
}

thread_local std::unique_ptr<LoggerStream::SmallBuffer> LoggerStream::buffer_ = std::make_unique<LoggerStream::SmallBuffer>();

template<typename T>
void LoggerStream::ConvertInterger(T t) {
  if (buffer_->RemainLen() > kMaxNumbericSize) {
    size_t len = Convert(buffer_->GetCurrent(), t);
    buffer_->MoveCursor(len);
  }
}

LoggerStream::LoggerStream() {
  ResetBuffer();
}

LoggerStream::SmallBuffer& LoggerStream::Buffer() {
  return *(buffer_);
}
bool LoggerStream::Append(const char* data, uint32_t len) {
  return buffer_->Append(data, len);
}

LoggerStream& LoggerStream::operator<<(short v) {
  ConvertInterger(v);
  return *this;
}

LoggerStream& LoggerStream::operator<<(unsigned short v) {
  ConvertInterger(v);
  return *this;
}

LoggerStream& LoggerStream::operator<<(int v) {
  ConvertInterger(v);
  return *this;
}

LoggerStream& LoggerStream::operator<<(unsigned int v) {
  ConvertInterger(v);
  return *this;
}

LoggerStream& LoggerStream::operator<<(long v) {
  ConvertInterger(v);
  return *this;
}

LoggerStream& LoggerStream::operator<<(unsigned long v) {
  ConvertInterger(v);
  return *this;
}

LoggerStream& LoggerStream::operator<<(long long v) {
  ConvertInterger(v);
  return *this;
}

LoggerStream& LoggerStream::operator<<(unsigned long long v) {
  ConvertInterger(v);
  return *this;
}
LoggerStream& LoggerStream::operator<<(const void* v) {
  if (buffer_->RemainLen() > kMaxNumbericSize) {
    buffer_->Append("0x", 2);
    ConvertHex(buffer_->GetCurrent(), reinterpret_cast<uintptr_t>(v));
  }
  return *this;
}
LoggerStream& LoggerStream::operator<<(double v) {
  // todo: markxiao
  if (buffer_->RemainLen() > kMaxNumbericSize) {
    floaxie::ftoa(v, buffer_->GetCurrent());
  }
  return *this;
}
LoggerStream& LoggerStream::operator<<(float v) {
  if (buffer_->RemainLen() > kMaxNumbericSize) {
    floaxie::ftoa(v, buffer_->GetCurrent());
  }
  return *this;
}
LoggerStream& LoggerStream::operator<<(char v) {
  buffer_->Append(v);
  return *this;
}
LoggerStream& LoggerStream::operator<<(const char* v) {
  if (v) {
    uint32_t len = strlen(v);
    buffer_->Append(v, len);
  } else {
    buffer_->Append("(null)", 6);
  }
  return *this;
}

LoggerStream& LoggerStream::operator<<(const unsigned char* v) {
  return operator<<(reinterpret_cast<const char*>(v));
}
LoggerStream& LoggerStream::operator<<(const std::string& v) {
  buffer_->Append(v.c_str(), v.size());
  return *this;
}


}
